package com.cherry.aop.factory;

import com.cherry.aop.service.IAccountService;
import com.cherry.aop.util.TransactionManager;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * jdk内置动态代理，只能基于接口实现
 */
public class BeanFactory {

    private TransactionManager transactionManager;
    private IAccountService accountService;

    public void setTransactionManager(TransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public void setAccountService(IAccountService accountService) {
        this.accountService = accountService;
    }

    public IAccountService proxyAccountService() {
        /**
         * 代理：
         * 间接。
         * 获取代理对象：
         * 要求：
         * 被代理类最少实现一个接口
         * 创建的方式
         * Proxy.newProxyInstance(三个参数)
         * 参数含义：
         * ClassLoader：和被代理对象使用相同的类加载器。
         * Interfaces：和被代理对象具有相同的行为。实现相同的接口。
         * InvocationHandler：如何代理。
         * 策略模式：使用场景是：
         * 数据有了，目的明确。
         * 如何达成目标，就是策略。
         *
         */
        return (IAccountService) Proxy.newProxyInstance(accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * 执行被代理对象的任何方法，都会经过该方法。
                     * 此方法有拦截的功能。
                     *
                     * 参数：
                     * proxy：代理对象的引用。不一定每次都用得到
                     * method：当前执行的方法对象
                     * args：执行方法所需的参数
                     * 返回值：
                     * 当前执行方法的返回值
                     */
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //生成实例会先调用动态代理方法
                        //过滤方法不执行代理
                        if ("test".equals(method.getName())) {
                            return method.invoke(accountService, args);
                        }
                        Object rtValue = null;
                        try {
                            transactionManager.beginTransaction();
                            rtValue = method.invoke(accountService, args);
                            transactionManager.commit();
                            return rtValue;
                        } catch (Exception e) {
                            e.printStackTrace();
                            transactionManager.rollback();
                        } finally {
                            transactionManager.release();
                        }
                        return null;
                    }
                });

    }

}
